package com.ingenico.insider.services.impl;

import java.util.Hashtable;
import java.util.Iterator;
import java.util.MissingResourceException;
import java.util.logging.Logger;

import javax.swing.JCheckBoxMenuItem;
import javax.swing.JMenu;

import net.infonode.docking.DockingWindow;
import net.infonode.docking.DockingWindowAdapter;
import net.infonode.docking.View;

import com.ingenico.insider.awt.event.ViewsMenuActionListener;
import com.ingenico.insider.docking.DynamicView;

public class ViewsMenuSupplier {
	private static final Logger _logger = Logger.getLogger(ViewsMenuSupplier.class.getCanonicalName());

	/**
	 * Public constant keywords
	 */
	public static final String VIEWS_KEY = "views";

	/**
	 * singleton instance
	 */
	private static ViewsMenuSupplier instance;

	/**
	 * Listener class that updates menu items depending on docking window events
	 * 
	 * @author lvictor
	 */
	private final class DockListener extends DockingWindowAdapter {
		private void updateMenuItem(final DockingWindow window) {
			// Bug workaround
			if (window == null)
				return;

			// For dynamic views, we simply set the menu item check-box depending on the view status 
			if (window instanceof DynamicView) {
				final DynamicView view = (DynamicView) window;

				// Retrieve the current menuItem for this view
				JCheckBoxMenuItem menuItem;
				menuItem = viewItems.get(view);
				// If none was available, then we create an item for the view
				if (menuItem == null) {
					menuItem = add(view);
				} else {
					menuItem.setState(view.getRootWindow() != null);
				}
			} else {
				//  If the window is a split window or a tab window we propagate
				// the update process down to its children.
				for (int i = 0; i < window.getChildWindowCount(); i++) {
					updateMenuItem(window.getChildWindow(i));
				}
			}
		}

		@Override
		public void viewFocusChanged(View previouslyFocusedView, View focusedView) {
			updateMenuItem(focusedView);
		}

		@Override
		public void windowAdded(DockingWindow addedToWindow, DockingWindow addedWindow) {
			updateMenuItem(addedWindow);
		}

		@Override
		public void windowRemoved(DockingWindow removedFromWindow, DockingWindow removedWindow) {
			updateMenuItem(removedWindow);
		}
	};

	/**
	 * The associated menu action listener. There is one action listener per view menu.
	 */
	private final ViewsMenuActionListener viewsActionListener;

	/**
	 * The views menu that the one and only instance of ThemesMenuSupplier manages
	 */
	private final JMenu viewsMenu;

	/**
	 * A hash table that links a menu item to its associated view.
	 */
	private final Hashtable<DynamicView, JCheckBoxMenuItem> viewItems;

	/**
	 * private constructor of final class to prevent external instantiation
	 */
	private ViewsMenuSupplier() {
		// Create the table that links views to the associated menu item
		viewItems = new Hashtable<DynamicView,JCheckBoxMenuItem>();

		// Get/Build the standard view menu and create the menu action listener that will handle its events
		viewsMenu = getViewsMenu();
		viewsActionListener = new ViewsMenuActionListener();
		_logger.finer("views menu = " + viewsMenu);

		// Initialize the menu with the current docked views
		_logger.fine("Initializing views menu.");
		initViewsMenu();

		// add the views menu under the windows menu
		_logger.fine("Registering views menu under the windows menu.");
		StandardMenuSupplier.getInstance().getWindowsMenu().add(viewsMenu);

		// Register a listener to track window/views events (view added, view closed) and update the views menu
		DockableViewsSupplier.getInstance().addDockingWindowListener(new DockListener());
	};

	/**
	 * Initialize the menu with the current docked views
	 */
	private void initViewsMenu() {
		final Iterator<DynamicView> i = DockableViewsSupplier.getInstance().getViewsIterator();
		while (i.hasNext()) {
			DynamicView view = (DynamicView) i.next();
			add(view);
		}
	}

	/**
	 * Internal method to add a view to the views menu
	 * 
	 * This method do not add the view to the docking windows frame
	 * 
	 * @param view the view that needs to be added to the menu
	 * @return a created menuItem or the menu item currently registered with this view
	 */
	private JCheckBoxMenuItem add(DynamicView view) {
		final JCheckBoxMenuItem menuItem;

		menuItem = new JCheckBoxMenuItem(view.getTitle());
		menuItem.setActionCommand(Integer.toString(view.getViewId()));
		menuItem.setToolTipText(view.getToolTipText());
		menuItem.setIcon(view.getIcon());
		menuItem.setState(view.getRootWindow() != null);
		menuItem.addActionListener(viewsActionListener);

		_logger.fine("Adding new view : \"" + menuItem.getText() + "\".");
		viewItems.put(view, menuItem);
		viewsMenu.add(menuItem);
		
		return menuItem;
	}

	/**
	 * retrieve the singleton instance
	 *
	 * @return the ViewsMenuSupplier singleton
	 */
	public static ViewsMenuSupplier getInstance() {
		if (instance == null) {
			_logger.info(ViewsMenuSupplier.class.getName() + " instance does not exist... Creating instance.");
			instance = new ViewsMenuSupplier();
		}
		return instance;
	}

	/**
	 * Convenience method that returns the themes menu
	 * 
	 * @return the themes menu
	 */
	public JMenu getViewsMenu () throws MissingResourceException {
		return StandardMenuSupplier.getInstance().getStandardMenu(VIEWS_KEY);
	}
}
